package pso.gui.visualization;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.Comparator;

import javax.swing.SwingUtilities;

import org.jfree.data.xy.XYSeries;

import pso.PSOAlgorithm;
import pso.particle.Particle;

public class ConvergenceChartSeries extends XYSeries implements
		PropertyChangeListener {

	private static final long serialVersionUID = 1184716912217612106L;
	private final StatisticsType type;

	public ConvergenceChartSeries(PSOAlgorithm pso, String label,
			StatisticsType type) {
		super(label);
		pso.addPropertyChangeListener("iteration", this);
		this.type = type;
		setNotify(false);
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if ("iteration".equals(evt.getPropertyName())) {

			PSOAlgorithm source = (PSOAlgorithm) evt.getSource();
			final int currentIteration = source.getCurrentIteration();

			double value = 0;
			if (type == StatisticsType.BEST) {
				final double bestValue = source.getSwarm()[0]
						.getFitnessFunction().convertToRealValue(
								source.getSwarm()[0].getNeighborhood()
										.getBestValue());
				value = bestValue;
			} else if (type == StatisticsType.MEDIAN) {
				Particle[] neighborhood = source.getSwarm()[0]
						.getNeighborhood().getNeighbours();
				Particle[] particlesToSort = Arrays.copyOf(neighborhood,
						neighborhood.length);
				Arrays.sort(particlesToSort, new Comparator<Particle>() {

					@Override
					public int compare(Particle o1, Particle o2) {
						return Double.compare(o1.getFitness(), o2.getFitness());
					}
				});
				value = source.getSwarm()[0].getFitnessFunction()
						.convertToRealValue(
								particlesToSort[particlesToSort.length / 2]
										.getFitness());
			}
			final double valueForChart = value;

			final int totalIterations = source.getStopCondition()
					.getParameter();
			final int[] updatingIterations = { totalIterations / 4,
					totalIterations / 2, (totalIterations / 4) * 3,
					totalIterations };

			SwingUtilities.invokeLater(new Runnable() {

				@Override
				public void run() {
					for (int i = 0; i < updatingIterations.length; i++) {
						if (currentIteration == updatingIterations[i]) {
							setNotify(true);
							break;
						}
					}
					add(currentIteration, valueForChart);
					if (getNotify()) {
						setNotify(false);
					}
				}
			});

		}
	}

	public enum StatisticsType {
		BEST, MEDIAN
	}
}
